﻿using System;
using System.Collections.Generic;
using System.Text;
using FireT.FMOD;
using PacMan3D.Utils;

namespace PacMan3D.Sounds
{
    public class SoundController
    {
        private static SoundController instance;

        private SoundController() { }

        public static SoundController Instance()
        {
            if (instance == null)
                instance = new SoundController();

            return instance;
        }

        private FireT.FMOD.System System = null;
        
        private Channel HitsChannel = null;
        private Channel MusicChannel = null;

        private Sound CurrentMusic;

        private Dictionary<SoundEventType, Sound> HitsSounds = new Dictionary<SoundEventType, Sound>();

        /// <summary>
        /// 
        /// </summary>
        public void Initialize()
        {
            RESULT result;
            
            result = Factory.System_Create(ref this.System);
            CheckForError(result);

            uint version = 0;
            result = this.System.getVersion(ref version);
            CheckForError(result);

            if (version < VERSION.number) {
                throw new ApplicationException("SoundController: You are using an old version of FMOD " + version.ToString("X") + ". This program requires " + VERSION.number.ToString("X") + ".");
            }

            result = this.System.init(2, INITFLAG.NORMAL, (IntPtr)null);
            CheckForError(result);

            LoadSounds();
        }

        private static void CheckForError(RESULT result)
        {
            if (result != RESULT.OK)
                throw new ApplicationException("SoundController: " + Errors.String(result));
        }

        private void LoadSounds()
        {
            LoadSound(SoundEventType.MENU_OPTION, Constants.SOUND_MENU_OPTION);
            LoadSound(SoundEventType.MENU_ENTER, Constants.SOUND_MENU_ENTER);

            LoadSound(SoundEventType.PACMAN_WALL, Constants.SOUND_PACMAN_WALL);
            LoadSound(SoundEventType.PACMAN_BIG_FOOD, Constants.SOUND_PACMAN_BIG_FOOD);
            LoadSound(SoundEventType.PACMAN_FOOD, Constants.SOUND_PACMAN_FOOD);
            LoadSound(SoundEventType.PACMAN_GHOST, Constants.SOUND_PACMAN_GHOST);
            LoadSound(SoundEventType.PACMAN_LIFE, Constants.SOUND_PACMAN_LIFE);

            LoadSound(SoundEventType.GAME_OVER_WIN_1, Constants.SOUND_GAME_OVER_WIN_1);
            LoadSound(SoundEventType.GAME_OVER_WIN_2, Constants.SOUND_GAME_OVER_WIN_2);
            LoadSound(SoundEventType.GAME_OVER_LOOSE_1, Constants.SOUND_GAME_OVER_LOOSE_1);
            LoadSound(SoundEventType.GAME_OVER_LOOSE_2, Constants.SOUND_GAME_OVER_LOOSE_2);
        }

        private void LoadSound(SoundEventType eventType, string path)
        {
            Sound sound = null;
            RESULT result = this.System.createSound(path, MODE.DEFAULT, ref sound);
            CheckForError(result);
            this.HitsSounds.Add(eventType, sound);
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="eventType"></param>
        public void PlaySound(SoundEventType eventType)
        {
            Sound sound;
            if (!this.HitsSounds.TryGetValue(eventType, out sound))
                throw new ApplicationException("SoundController: " + sound + " sound not yet loaded.");
           
            RESULT result = this.System.playSound(CHANNELINDEX.REUSE, sound, false, ref this.HitsChannel);
            CheckForError(result);
        }

        /// <summary>
        /// 
        /// </summary>
        public void PlayMusic()
        {
            RESULT result;
            if (this.MusicChannel != null)
            {
                bool isPlaying = false;
                result = this.MusicChannel.isPlaying(ref isPlaying);
                CheckForError(result);
                if (isPlaying)
                    return;
            }

            if (this.CurrentMusic != null)
            {
                result = this.CurrentMusic.release();
                CheckForError(result);
            }
            
            result = this.System.createSound(Constants.SOUND_MUSIC, MODE.SOFTWARE | MODE.CREATECOMPRESSEDSAMPLE | MODE.LOOP_NORMAL, ref this.CurrentMusic);
            CheckForError(result);
            
            result = this.System.playSound(CHANNELINDEX.FREE, this.CurrentMusic, false, ref this.MusicChannel);
            CheckForError(result);
        }

        /// <summary>
        /// 
        /// </summary>
        public void StopMusic()
        {
            if (this.MusicChannel == null)
                return;

            RESULT result = this.MusicChannel.stop();
            CheckForError(result);

            result = this.CurrentMusic.release();
            CheckForError(result);

            this.MusicChannel = null;
        }

        public void Update()
        {
            this.System.update();
        }

        public void Deinitialize()
        {
            Sound sound;
            RESULT result;

            foreach (SoundEventType key in this.HitsSounds.Keys)
            {
                this.HitsSounds.TryGetValue(key, out sound);
                result = sound.release();
                CheckForError(result);
            }
            this.HitsSounds.Clear();

            if (this.CurrentMusic != null)
                this.CurrentMusic.release();

            this.System.release();
        }

        public enum SoundEventType { MENU_OPTION, MENU_ENTER, PACMAN_WALL, PACMAN_FOOD, PACMAN_BIG_FOOD, PACMAN_LIFE, PACMAN_GHOST, GAME_OVER_LOOSE_1, GAME_OVER_LOOSE_2, GAME_OVER_WIN_1, GAME_OVER_WIN_2 }
    }
}
